The library SysTimeCore supports functions for handling the difference (delta) between two points in time (duration) provided by the CODESYS runtime system.
The SysTime alias type is defined to handle ULINT values (0 - 18.446.744.073.709.551.615).
xOK := (SIZEOF(SysTime) = 8 (* Bytes *)) AND (SIZEOF(ULINT) = 8 (* Bytes *));
Note
In the following examples the prefix st was selected for variables of type SysTime.
See: Naming Conventions for variables of type UDINT, ULINT, TIME and LTIME.
Returns a timer tick value (point in time) of type UDINT in a resolution of one millisecond ([ms]).
This results in a value range of 4.294.967.296ms ≈ 4.294.967s ≈ 7.582m ≈ 1.193h ≈ 49d
Note
The value range of the data type TIME corresponds to the value range of the data type UDINT with a resolution of one millisecond.
Note
Instead of the function SysTimeGetMs, the operator TIME() can also be used.
VAR
tDelta, tStart, tEnd : TIME;
END_VAR
tStart := TO_TIME(SysTimeGetMs());
(* ... lengthy operation ... *)
tEnd := TO_TIME(SysTimeGetMs());
tDelta := tEnd - tStart (* ms *);
The code example in the last section has the same effect as the code example in the next section.
tStart := TIME();
(* ... lengthy operation ... *)
tDelta := TIME() - tStart (* ms *);
Returns a timer tick value (point in time) of type SysTime in a resolution of one microsecond ([µs]).
This results in a value range of 18.446.744.073.709.551.615µs ≈ 18.446.744.073.709.551ms ≈ 18.446.744.073.709s ≈ 307.445.734.561m ≈ 5.124.095.576h ≈ 213.503.982d ≈ 599.730y
VAR
stDelta, stStart, stEnd : SysTime;
END_VAR
SysTimeGetUs(stStart);
(* ... lengthy operation ... *)
SysTimeGetUs(stEnd);
stDelta := stEnd - stStart (* µs *);
Returns a timer tick value (point in time) of type SysTime in a resolution of one nanosecond ([ns]).
This results in a value range of 18.446.744.073.709.551.615ns ≈ 18.446.744.073.709.551µs ≈ 18.446.744.073.709ms ≈ 18.446.744.073s ≈ 307.445.734m ≈ 5.124.095h ≈ 213.503d ≈ 599y
Note
The value range of the data type LTIME corresponds to the value range of the data type SysTime and ULINT with a resolution of one nanosecond.
Note
Instead of the function SysTimeGetNs, the operator LTIME() can also be used.
VAR
ltDelta : LTIME;
stStart, stEnd : SysTime;
END_VAR
SysTimeGetNs(stStart);
(* ... lengthy operation ... *)
SysTimeGetNs(stEnd);
ltDelta := TO_LTIME(stEnd - stStart) (* ns *);
The code example in the last section has the same effect as the code example in the next section.
VAR
ltDelta, ltStart : LTIME;
END_VAR
ltStart := LTIME();
(* lengthy operation *)
ltDelta := LTIME() - ltStart (* ns *);
The handling of a time duration at the basis of the data types TIME and LTIME are also the kernel of the timer function blocks in the libraries standard (for TIME) and standard64 (for LTIME).
Example
PROGRAM TIMER
VAR
tDelta : TIME := T#2s;
T1 : TON;
T2 : TP;
T3 : TOF;
END_VAR
The major problem dealing with the localtime is that certain times of a day may occur twice in a year. For example, in the US/Eastern timezone on the last Sunday morning in October, the following sequence happens:
01:00 EDT occurs
1 hour later, instead of 2:00am the clock is turned back 1 hour and 01:00 happens again (this time 01:00 EST)
In fact, every instant between 01:00 and 02:00 occurs twice. This means that if a variable of type "TIME" is written in the "US/Eastern" time zone, it can no longer be decided whether it was written shortly before or after the transition from summer time to winter time.
The best and simplest solution is to stick with using UTC. Note that some other timezones are commonly thought of as the same (GMT, Greenwich, Universal, etc.). The definition of UTC is distinct from these other timezones, and they are not equivalent.
"UTC" is Coordinated Universal Time. It is a successor to, but distinct from, Greenwich Mean Time (GMT) and the various definitions of Universal Time. UTC is currently the worldwide standard for regulating clocks and time measurement.
All other timezones are defined relative to UTC, and include offsets like UTC+0800 - hours to add or subtract from UTC to derive the local time. No daylight saving time occurs in UTC, making it a useful timezone to perform date arithmetic without worrying about the confusion and ambiguities caused by daylight saving time transitions, your country changing its timezone, or mobile applications that roam through multiple timezones.
The SysTimeRTC Library provides a set of functions to deal with this issues and helps to handle variables of the types DATE, TIME_OF_DAY and DATE_AND_TIME correctly.
CODESYS is following the IEC 61131-3 standard and implements therefore some data types:
DATE: Seconds since Thursday, 1.1.1970 00:00:00, managed in a 32 Bit data type like UDINT
TIME_OF_DAY or TOD: Milliseconds since 00:00:00.000, managed in a 32 Bit data type like UDINT
DATE_AND_TIME or DT: Seconds since Thursday, 1.1.1970 00:00:00, managed in a 32 Bit data type like UDINT
Starting with data type DATE_AND_TIME, variables of the other data types can be assigned accordingly. A variable of type DATE_AND_TIME can be initialized with a current value with the help of the CODESYS runtime system's functions SysTimeRtcGet and SysTimeRTCConvertUTCToLocal.
See: Naming Conventions for variables of type UDINT, DATE, TIME_OF_DAY and DATE_AND_TIME.
VAR
udiUTC_DateAndTime : UDINT;
udiLocal_DateAndTime : UDINT;
Result : RTS_IEC_RESULT;
dtNow : DATE_AND_TIME;
todNow : TIME_OF_DAY;
datNow : DATE;
END_VAR
udiUTC_DateAndTime := TO_UDINT(SysTimeRtcGet(Result)); // UDINT#1528268918
Result := SysTimeRTCConvertUTCToLocal(udiUTC_DateAndTime, udiLocal_DateAndTime); // UDINT#1528276118
dtNow := TO_DT(udiLocal_DateAndTime); // DT#2018-6-6-9:8:38
todNow := TO_TOD(dtNow); // TOD#9:8:38
datNow := TO_DATE(dtNow); // D#2018-6-6
The CODESYS runtime system specifies some additional data types:
Standard udiTimeStamp (UDINT) Seconds since Thursday, 1.1.1970 00:00:00
High Resolution uliTimeStamp (ULINT) Milliseconds since Thursday, 1.1.1970 00:00:00.000
SysTimeDate (STRUCT)
wYear : UINT; // Year (e.g. 2006)
wMonth : UINT; // Month (1..12: January = 1, December = 12)
wDay : UINT; // Day of month (1..31)
wHour : UINT; // Hours after midnight (0..23)
wMinute : UINT; // Minutes after hour (0..59)
wSecond : UINT; // Seconds after minute (0..59)
wMilliseconds : UINT; // Milliseconds after second (0..999). Optional!
wDayOfWeek : UINT; // Day of week (1..7: Monday = 1, Sunday = 7
wYday : UINT; // Day of year (1..365): January 1 = 1, December 31 = 364/365
See: SYSTEMTIME structure
The CODESYS runtime system provides the following functions for getting the current date and time in the UTC timezone:
SysTimeRtcGet: Returns the date and time in seconds since Thursday, 1.1.1970 00:00:00, managed in a 32 Bit data type like UDINT
SysTimeRtcHighResGet: Returns the date and time in milliseconds since Thursday, 1.1.1970 00:00:00.000, managed in a 64 Bit data type like SysTime
Converting from UTC to Localtime
Note
The concept "local timezone" in the context of the CODESYS runtime system is based on the time zone witch the local controller is configured for. This is not always the time zone suitable for displaying the current time. Some times it is necessary to use the timezone which the CODESYS development environment is configured for. Some times there are other requirements to use a completely different time zone.
In order to meet these conflicting requirements, the util.library offers the option of using the functions for converting time and date data with a flexible specification of a freely selectable time zone.
The CODESYS runtime system provides the following functions for converting the current date and time from the UTC timezone to the local timezone:
SysTimeRtcConvertHighResToLocal:
SysTimeRtcConvertDateToHighRes:
Note
In the following examples the prefix std was selected for variables of type SysTimeDate.
VAR
stUTC_Timestamp : SysTime;
stLocal_TimeStamp : SysTime;
stdNow : SysTimeDate;
Result : RTS_IEC_RESULT;
dtNow : DATE_AND_TIME;
todNow : TIME_OF_DAY;
datNow : DATE;
END_VAR
Result := SysTimeRtcHighResGet(stUTC_Timestamp); // ULINT#1528273494913
Result := SysTimeRtcConvertHighResToLocal(stUTC_Timestamp, stdNow);
// stdNow.wYear = UINT#2018
// stdNow.wMonth = UINT#6
// stdNowy.wDay = UINT#6
// stdNow.wHour = UINT#10
// stdNow.wMinute = UINT#24
// stdNow.wSecond = UINT#54
// stdNow.wMilliseconds = UINT#913
// stdNow.wDayOfWeek = UINT#3
// stdNow.wYday = UINT#157
Result := SysTimeRtcConvertDateToHighRes(stdNow, stLocal_TimeStamp); // ULINT#1528280694913
dtNow := TO_DT(stLocal_TimeStamp / 1000 (* ms *)); // DT#2018-6-6-10:24:54
todNow := TO_TOD(stLocal_TimeStamp MOD TO_ULINT(T#1D)); // TOD#10:24:54.913
datToday := TO_DATE(dtNow); // D#2018-6-6
Converting from Localtime to UTC
The CODESYS runtime system provides the following function for converting the current date and time from the local timezone to the UTC timezone:
SysTimeRtcConvertLocalToHighRes: Converts the date and time given by SysTimeDate structure (localtime) into a high resolution time of type SysTime (UTC)
VAR
stdNew : SysTimeDate;
stUTC_Timestamp : SysTime;
END_VAR
// stdNew.wYear = UINT#2018
// stdNew.wMonth = UINT#6
// stdNew.wDay = UINT#6
// stdNew.wHour = UINT#10
// stdNew.wMinute = UINT#24
// stdNew.wSecond = UINT#54
// stdNew.wMilliseconds = UINT#913
Result := SysTimeRtcConvertLocalToHighRes(stdNew, stUTC_Timestamp); // ULINT#1528273494913
// Writing a high resolution value of type ``SysTime`` to the realtime clock (RTC) device.
Result := SysTimeRtcHighResSet(stUTC_Timestamp);
To handle the local time, it is neccecary to always specify the time and date and the time zone that is currently valid. This makes it possible to convert the local time to Coordinated Universal Time and vice versa. UTC is Coordinated Universal Time. It is a successor to, but distinct from, Greenwich Mean Time (GMT) and the various definitions of Universal Time. UTC is now the worldwide standard for regulating clocks and time measurement.
All other timezones are defined relative to UTC, and include offsets like UTC+0800 - hours to add or subtract from UTC to derive the local time. No daylight saving time occurs in UTC, making it a useful timezone to perform date arithmetic without worrying about the confusion and ambiguities caused by daylight saving time transitions, your country changing its timezone, or mobile computers that roam through multiple timezones.
The following snippet exposes the definition of UTC and CentralEuropeTime/CentralEuropeSommerTime.
VAR_GLOBAL CONSTANT
/// Coordinated Universal Time
gc_tzTimeZoneUTC : TimeZone := (asgPeriod := [(sName:='UTC')]);
/// Central Europe Time
gc_tzTimeZoneCET : TimeZone :=
(
iBias := 60 (* T#1M => minutes *),
asgPeriod := [
( (* (CEST -> CET) - Last Sunday in Oktober at 03:00:00.000 (CEST) *)
sName:='CET',
dtDate := (uiMonth := 10, eWeekday := WEEKDAY.SUNDAY, uiDay := 5, uiHour := 3)
),( (* (CET -> CEST) - Last Sunday in March at 02:00:00.000 (CET) *)
sName := 'CEST',
dtDate := (uiMonth := 3, eWeekday := WEEKDAY.SUNDAY, uiDay := 5, uiHour := 2),
iBias := 60 (* T#1M => minutes *)
)]
);
END_VAR
Note
The Bias element represents the offset from the Coordinated Universal Time (UTC). This value is in minutes.
The offset growing positive in eastern direction starting from the prime meridian.
The offset is growing negative in western direction starting from the prime meridian.
With the data structure TimeZone it is possible to specify every timezone of the world and so the functions below can handle the local time conversion and the switching from standard to day light saving period's if necessary.
Example
With the following expressions the difference between the UTC time zone and an other timezone instance can be calculated.
iBiasUTC_Standard := gc_tzTimeZoneCET.iBias;
iBiasUTC_Daylight := gc_tzTimeZoneCET.iBias + gc_tzTimeZoneCET.asgPeriod[PERIOD.DAYLIGHT].iBias;
Note
The time zone which the current computer is configured for, is not always the time zone suitable for displaying the current time. Therefore in the respective application, the possibility should be provided, to be able to select the "correct" time zone that is suitable for the related output option (WebVisu, LogFiles, Email, ...).
In version 3.5.14.0 and higher, the Util library provides the following functions:
Conversion from UTC timestamp (e.g. from SysTimeRtcHighResGet) to the current period and the local date and time related to the TimeZone parameter.
FUNCTION PUBLIC LocalDateTime : ULINT
VAR_IN_OUT CONSTANT
tzTimeZone : TimeZone;
END_VAR
VAR_INPUT
uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
eErrorID : ERROR;
ePeriod : PERIOD; (* 1 = ``PERIOD.STANDARD``, 2 = ``PERIOD.DAYLIGHT`` *)
END_VAR
Splitting a timestamp into the components of a point in.
FUNCTION PUBLIC SplitDateTime : ERROR
VAR_INPUT
uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
uiYear : YEAR; (* 1970..2106 *)
uiMonth : MONTH;
uiDay : DAY;
uiHour : HOUR;
uiMinute : MINUTE;
uiSecond : SECOND;
uiMilliseconds : MILLISECOND;
eWeekday : WEEKDAY;
END_VAR
Joining the components of a point in time to a timestamp.
FUNCTION PUBLIC JoinDateTime : ULINT
VAR_INPUT
uiYear : YEAR; (* 1970..2106 *)
uiMonth : MONTH;
uiDay : DAY;
uiHour : HOUR;
uiMinute : MINUTE;
uiSecond : SECOND;
uiMilliseconds : MILLISECOND;
END_VAR
VAR_OUTPUT
eErrorID : ERROR := ERROR.NO_ERROR;
END_VAR
Separates a timestamp in its IEC data typed parts.
FUNCTION PUBLIC SeparateDateTime : ERROR
VAR_INPUT
uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
eWeekDay : WEEKDAY;
datDate : DATE;
todTime : TIME_OF_DAY;
END_VAR
Combines the IEC data typed parts to a timestamp.
FUNCTION PUBLIC CombineDateTime : ULINT
VAR_INPUT
datDate : DATE;
todTime : TIME_OF_DAY;
END_VAR
VAR_OUTPUT
eErrorID : ERROR;
END_VAR
Calculates the appropriate value of the WEEKDAY enum that matches the parameter datDate.
FUNCTION PUBLIC DayOfWeek : WEEKDAY
VAR_INPUT
datDate : DATE;
END_VAR
VAR_OUTPUT
eErrorID : ERROR;
END_VAR
Checks whether the year (uiYear) passed represents a leap year.
FUNCTION PUBLIC IsLeapYear : BOOL
VAR_INPUT
uiYear : YEAR; (* 1970..2106 *)
END_VAR
VAR_OUTPUT
eErrorID : ERROR;
END_VAR